/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tpevol;

import genetic.Chromosome;
import java.util.Set;
import tpgen.grammar.Node;
import tpgen.grammar.Symbol;
import tpgen.grammar.Variable;
import tpgen.grammar.Variables;
import tpgen.patterns.Axiom;
import tpgen.patterns.Eq;
import tpgen.patterns.EqHet;
import tpgen.patterns.OntologyPattern;
import tpgen.patterns.PatternTransformation;
import tpgen.patterns.Transformation;
import tpgen.patterns.TransformationPattern;

/**
 *
 * @author shanki
 */
public class TpChromosome extends Chromosome<TpChromosome, TpContext> {

    private TransformationPattern tp;

    public TpChromosome(TransformationPattern tp) {
        this.tp = tp;
    }
    
    @Override
    public TpChromosome[] reproduce(TpChromosome chromosome, TpContext context, double pMutate) {
        if (chromosome == null) throw new IllegalArgumentException("chromosome cannot be null");
        
        TransformationPattern[] t   = crossover(this.tp, chromosome.tp, context);
        
        t[0]                        = mutate(t[0], context, pMutate);
        t[1]                        = mutate(t[1], context, pMutate);
        
        t[0]                        = fix(t[0]);
        t[1]                        = fix(t[1]);

        TpChromosome[] chromosomes  = new TpChromosome[2];
        chromosomes[0]              = new TpChromosome(t[0]);
        chromosomes[1]              = new TpChromosome(t[1]);
        
        return chromosomes;        
    }
    
    private TransformationPattern fix(TransformationPattern tp) {
        Set<Variable> sVars = tp.getSourceOp().getVars();
        Set<Variable> tVars = tp.getTargetOp().getVars();
        
        PatternTransformation pt    = new PatternTransformation();
        
        for (Transformation t : tp.getPt()) {
            if (sVars.contains(t.getLeft()) &&
                tVars.contains(t.getRight())) {
                pt.add(t);
            }
        }
        
        return new TransformationPattern(tp.getSourceOp(), tp.getTargetOp(), pt);
    }

    @Override
    protected double computeValue(TpContext context) {
        return context.evaluate(tp);
    }

    private TransformationPattern[] crossover(TransformationPattern tpA, TransformationPattern tpB, TpContext context) {
        OntologyPattern[] sourseOps = crossover(tpA.getSourceOp(), tpB.getSourceOp(), context);
        OntologyPattern[] targetOps = crossover(tpA.getTargetOp(), tpB.getTargetOp(), context);
        PatternTransformation[] pts = crossover(tpA.getPt(), tpB.getPt(), context);
        
        TransformationPattern tpA2  = new TransformationPattern(sourseOps[0], targetOps[0], pts[0]);
        TransformationPattern tpB2  = new TransformationPattern(sourseOps[1], targetOps[1], pts[1]);
        
        return new TransformationPattern[] { tpA2, tpB2 };
    }
    
    private OntologyPattern[] crossover(OntologyPattern op1, OntologyPattern op2, TpContext context) {
        int crossPoint1 = context.randomCrosspoint(op1.size());
        int crossPoint2 = context.randomCrosspoint(op2.size());
        
        OntologyPattern newOp1  = new OntologyPattern();
        OntologyPattern newOp2  = new OntologyPattern();
        
        for (int i = 0; i < crossPoint1; ++i) {
            newOp1.add(op1.get(i));
        }
        
        for (int i = 0; i < crossPoint2; ++i) {
            newOp2.add(op2.get(i));
        }
        
        for (int i = crossPoint1; i < op1.size(); ++i) {
            newOp2.add(op1.get(i));
        }
        
        for (int i = crossPoint2; i < op2.size(); ++i) {
            newOp1.add(op2.get(i));
        }
        
        return new OntologyPattern[] { newOp1, newOp2 };
    }
    
    private PatternTransformation[] crossover(PatternTransformation pt1, PatternTransformation pt2, TpContext context) {
        int crossPoint1 = context.randomCrosspoint(pt1.size());
        int crossPoint2 = context.randomCrosspoint(pt2.size());
        
        PatternTransformation newPt1  = new PatternTransformation();
        PatternTransformation newPt2  = new PatternTransformation();
        
        for (int i = 0; i < crossPoint1; ++i) {
            newPt1.add(pt1.get(i));
        }
        
        for (int i = 0; i < crossPoint2; ++i) {
            newPt2.add(pt2.get(i));
        }
        
        for (int i = crossPoint1; i < pt1.size(); ++i) {
            newPt2.add(pt1.get(i));
        }
        
        for (int i = crossPoint2; i < pt2.size(); ++i) {
            newPt1.add(pt2.get(i));
        }
        
        return new PatternTransformation[] { newPt1, newPt2 };
    }

    private TransformationPattern mutate(TransformationPattern tpA, TpContext context, double pMutate) {
        OntologyPattern sourceOp    = mutate(tpA.getSourceOp(), context, pMutate);
        OntologyPattern targetOp    = mutate(tpA.getTargetOp(), context, pMutate);
        
        Variables varsAused = new Variables(sourceOp.getVars());
        Variables varsBused = new Variables(targetOp.getVars());        
        
        PatternTransformation pt    = mutate(tpA.getPt(), varsAused, varsBused, context, pMutate);
        
        return new TransformationPattern(sourceOp, targetOp, pt);
    }
    
    private OntologyPattern mutate(OntologyPattern op, TpContext context, double pMutate) {
        OntologyPattern newOp   = new OntologyPattern();
        
        for (Axiom axiom : op) {
            newOp.add(mutate(axiom, context, pMutate));
        }
        
        return newOp;
    }
    
    private Axiom mutate(Axiom axiom, final TpContext context, final double pMutate) {
        return axiom.reconstruct(new Node.NodeConstructor() {

            @Override
            public boolean reconstruct(Symbol symbol) {
                return context.randomBool(pMutate);
            }
        });
    }
    
    private PatternTransformation mutate(PatternTransformation pt, Variables varsAused, Variables varsBused, TpContext context, double pMutate) {
        PatternTransformation newPt = new PatternTransformation();
        
        for (Transformation t : pt) {
            newPt.add(mutate(t, varsAused, varsBused, context, pMutate));
        }
        
        return newPt;
    }
    
    private Transformation mutate(Transformation t, Variables varsAused, Variables varsBused, TpContext context, double pMutate) {
        Variable newA;
        Variable newB;
        
        if (context.randomBool(pMutate)) {
            newA = context.randomVariable(varsAused);
        } else {
            newA = t.getLeft();
        }
        
        if (context.randomBool(pMutate)) {
            newB = context.randomVariable(varsBused);
        } else {
            newB = t.getRight();
        }
        
        if (newA.sameType(newB)) {
            return new Eq(newA, newB);
        } else {
            return new EqHet(newA, newB);
        }
    }

    public TransformationPattern getTp() {
        return tp;
    }
    
}
